package com.retrozelda.nyanimals;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.RectF;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;

public class CRectangle extends INyanObject
{
	// ///////////////// OPENGL ///////////////////////////////
		// ////////////////////////////////////////////////////////
		// vertices
	protected static final int	FLOAT_SIZE_BYTES					= 4;
	protected static final int	TRIANGLE_VERTICES_DATA_STRIDE_BYTES	= 5 * FLOAT_SIZE_BYTES;
	protected static final int	TRIANGLE_VERTICES_DATA_POS_OFFSET	= 0;
	protected static final int	TRIANGLE_VERTICES_DATA_UV_OFFSET	= 3;
	protected final float[]		m_TriangleVerticesData				= {
				// X,	 Y, 	Z, 		U, 		V
				-1.0f,	-1.0f,	0.0f,	0.0f,	0.0f,	// 	right	top
				-1.0f, 	 1.0f,	0.0f,	0.0f,	1.0f,	// 	right	bottom
				 1.0f,	-1.0f,	0.0f, 	1.0f, 	0.0f,	// 	left	top
				 1.0f, 	 1.0f, 	0.0f, 	1.0f, 	1.0f};	//	left	bottom
																		

	protected FloatBuffer			m_TriangleVertices;

	// texture
	CTexture m_Texture;

	// shader
	protected static final String m_VertexShader =	 
			  "uniform mat4 uMVPMatrix;\n"
			+ "attribute vec4 aPosition;\n"
			+ "attribute vec2 aTextureCoord;\n"
			+ "varying vec2 vTextureCoord;\n"
			+ "void main() {\n"
			+ "  gl_Position = uMVPMatrix * aPosition;\n"
			+ "  vTextureCoord = aTextureCoord;\n"
			+ "}\n";

	protected static final String m_FragmentShader_Tex =					
			  "precision mediump float;\n"
			+ "varying vec2 vTextureCoord;\n"
			+ "uniform sampler2D sTexture;\n"
			+ "void main() {\n"
			+ "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n"
			+ "}\n";
	protected static final String m_FragmentShader_Color =					
			  "precision mediump float;\n"
			+ "uniform vec4 vColor;\n"
			+ "void main() {\n"
			+ "  gl_FragColor = vColor;\n"
			//+ "  gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
			+ "}\n";

	// shader handles
	protected int					m_ShaderProgram;
	protected int					m_MVPMatrixHandle;
	protected int					m_PositionHandle;
	protected int					m_TextureHandle;
	protected int					m_ColorHandle;
	
	// for solid color
	boolean 						m_solidColor;
	protected float					m_Red, m_Green, m_Blue, m_Alpha;

	// matrices
	protected float[]				m_MMatrix							= new float[16];		// model matrix
	protected float[]				m_MVPMatrix							= new float[16];		// model * view * proj
	protected float[]				m_position							= new float[2];
	protected float[]				m_scale								= new float[2];
	
	
	// settings
	protected boolean				m_bDraw = true;
	protected RectF 				m_BoundingRect;
	
	// Utilities
	int m_nScreenWidth;
	int m_nScreenHeight;
	
	public void UpdateScreenData(int width, int height)
	{
		m_nScreenWidth = width;
		m_nScreenHeight = height;
	}
	
	
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	public float[] GetModelMatrix() { return m_MMatrix;}
	public float[] GetMVPMatrix() { return m_MVPMatrix;}
	public void SetModelMatrix(float[] model) { m_MMatrix = model;}
	public void SetMVPMatrix(float[] mvp) { m_MVPMatrix = mvp;}
	
	public CTexture GetTexture() { return m_Texture;}
	public void SetTexture(CTexture tex) { m_Texture = tex;}
	
	public boolean IsDrawing() { return m_bDraw;}
	public void SetDraw(boolean bDraw) { m_bDraw = bDraw;}
	
	public CRectangle()
	{
		m_TriangleVertices = ByteBuffer.allocateDirect(m_TriangleVerticesData.length * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
		m_TriangleVertices.put(m_TriangleVerticesData).position(0);
		SetPosition(0, 0);
		SetSize(1, 1);
	}
	
	public Object clone()
	{
		
		// TODO Auto-generated constructor stub
		CRectangle ret = null;
		try
		{
			ret = (CRectangle)super.clone();
			ret.m_TriangleVertices = m_TriangleVertices.duplicate();

			// texture
			ret.m_Texture = m_Texture;
			
			// shader handles
			ret.m_ShaderProgram = m_ShaderProgram;
			ret.m_MVPMatrixHandle = m_MVPMatrixHandle;
			ret.m_PositionHandle = m_PositionHandle;
			ret.m_TextureHandle = m_TextureHandle;
			ret.m_ColorHandle = m_ColorHandle;
					
			// for solid color
			ret.m_solidColor = m_solidColor;
			ret.m_Red = m_Red;
			ret.m_Green = m_Green;
			ret.m_Blue = m_Blue;
			ret.m_Alpha = m_Alpha;

			// matrices
			ret.m_MMatrix = m_MMatrix.clone();		// model matrix
			ret.m_MVPMatrix = m_MVPMatrix.clone();		// model * view * proj
			ret.m_position = m_position.clone();
			ret.m_scale = m_scale.clone();
			
			ret.m_bDraw = m_bDraw;
		}
		catch (CloneNotSupportedException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return (CRectangle)ret;		
	}
	
	public int[] getPosition()
	{
		int[] tempPos = new int[2];
		tempPos[0] = (int)m_position[0];
		tempPos[1] = (int)m_position[1];
		return tempPos;
	}
	
	public void SetPosition(int posX, int posY)
	{
		m_position[0] = (float)posX;
		m_position[1] = (float)posY;
		BuildTouchBox();
	}

	public void SetSize()
	{
		m_scale[0] = (float)m_Texture.GetWidth();
		m_scale[1] = (float)m_Texture.GetHeight();
		BuildTouchBox();
	}
	
	// scale by percent
	public void Scale(float fScaleX, float fScaleY)
	{
		SetSize((int)((float)m_scale[0] * fScaleX), (int)((float)m_scale[1] * fScaleY));
	}
	
	public void SetSize(int Width, int Height)
	{
		if(Width < 0)
		{
			m_scale[0] = (float)m_Texture.GetWidth();
		}
		else
		{
			m_scale[0] = (float)Width;
		}
		
		if(Height < 0)
		{
			m_scale[1] = (float)m_Texture.GetHeight();
		}
		else
		{
			m_scale[1] = (float)Height;
		}
		BuildTouchBox();
		
	}
	public int[] GetSize()
	{
		int[] scale = new int[2];
		scale[0] = (int)m_scale[0];
		scale[1] = (int)m_scale[1];
		
		return scale;
	}
	
	public int[] GetTextureSize()
	{
		int[] size = new int[2];
		size[0] = m_Texture.GetWidth();
		size[1] = m_Texture.GetHeight();
		
		return size;
	}

	private void LoadTextureShader(NyanimalGLSurfaceView game)
	{
		// create shader program
		m_ShaderProgram = CNyanimalGLRender.createProgram(m_VertexShader, m_FragmentShader_Tex);
		if (m_ShaderProgram == 0)
		{
			return;
		}
	}
	private void LoadColorShader(NyanimalGLSurfaceView game)
	{
		// create shader program
		m_ShaderProgram = CNyanimalGLRender.createProgram(m_VertexShader, m_FragmentShader_Color);
		if (m_ShaderProgram == 0)
		{
			return;
		}
	}
	

	// RGBA is value between 0 and 255
	public void Init(NyanimalGLSurfaceView game, int R, int G, int B, int A)
	{
		// get the 0 - 255 to be 0.0 - 1.0
		m_Red = ((float)R / 255.0f);
		m_Green = ((float)G / 255.0f);
		m_Blue = ((float)B / 255.0f);
		m_Alpha = ((float)A / 255.0f);
		
		m_Texture = null;
		
		m_solidColor = true;
		LoadColorShader(game);
		FinishInit(game);
	}

	@Override
	public void Init(NyanimalGLSurfaceView game)
	{
		m_solidColor = false;
		LoadTextureShader(game);
		FinishInit(game);
	}

	private void FinishInit(NyanimalGLSurfaceView game)
	{

		// init the utilities
		UpdateScreenData(game.getWidth(), game.getHeight());
		
		// get positional handle
		m_PositionHandle = GLES20.glGetAttribLocation(m_ShaderProgram, "aPosition");
		if (m_PositionHandle == -1)
		{
			throw new RuntimeException("Could not get attrib location for aPosition");
		}

		if(m_solidColor == true)
		{
			// get Color handle
			if(m_solidColor == true)
			{
				m_ColorHandle = GLES20.glGetUniformLocation(m_ShaderProgram, "vColor");
				if (m_ColorHandle == -1)
				{
					throw new RuntimeException("Could not get attrib location for vColor");
				}
			}
		}
		else
		{
			// get texture handle
			m_TextureHandle = GLES20.glGetAttribLocation(m_ShaderProgram, "aTextureCoord");
			if (m_TextureHandle == -1)
			{
				throw new RuntimeException("Could not get attrib location for aTextureCoord");
			}
		}
		// get the mvp matrix handle
		m_MVPMatrixHandle = GLES20.glGetUniformLocation(m_ShaderProgram, "uMVPMatrix");
		if (m_MVPMatrixHandle == -1)
		{
			throw new RuntimeException("Could not get attrib location for uMVPMatrix");
		}
		
				
		// default the MVP
		Matrix.setIdentityM(m_MVPMatrix, 0);
	}
	
	@Override
	public void Update(float deltaTime)
	{
	}
	
	@Override
	public void Draw(CArgumentGroup arg)
	{
		
		// only continue if drawable
		if(m_bDraw == false)
		{
			return;
		}
		
		// NOTE: can do "event based"-like without setting to identity each frame
		////////////////////////////////////////
		// freshen the model matrix
		Matrix.setIdentityM(m_MMatrix, 0);
		
		// set the position
		Matrix.translateM(m_MMatrix, 0, m_position[0], m_position[1], 0.0f);
		
		// set any rotations
		
		// set any scales
		Matrix.scaleM(m_MMatrix, 0, m_scale[0], m_scale[1], 1.0f);
		////////////////////////////////////////
		
		// calculate the MVP
		Matrix.multiplyMM(m_MVPMatrix, 0, arg.GameClass.GetViewProjection(), 0, m_MMatrix, 0);
		
		
		// activate the stuff
		GLES20.glUseProgram(m_ShaderProgram);
		GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
		
		if(m_Texture != null)
		{
			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_Texture.GetTextureID());
		}

		// set the verts
		m_TriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
		GLES20.glVertexAttribPointer(m_PositionHandle, 3, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, m_TriangleVertices);

		// set the UVs
		m_TriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
		GLES20.glEnableVertexAttribArray(m_PositionHandle);

		// set the texture
		GLES20.glVertexAttribPointer(m_TextureHandle, 2, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, m_TriangleVertices);
		GLES20.glEnableVertexAttribArray(m_TextureHandle);
		
		// set the MVP
		GLES20.glUniformMatrix4fv(m_MVPMatrixHandle, 1, false, m_MVPMatrix, 0);

		// set teh color
		if(m_solidColor == true)
		{
			GLES20.glUniform4f(m_ColorHandle, m_Red, m_Green, m_Blue, m_Alpha );
		}
		
		// Draw
		GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

	}
	
	public void MakeTextureSize()
	{
		SetSize();
	}
	
	public void BuildTouchBox()
	{
		if(m_BoundingRect == null)
		{
			m_BoundingRect = new RectF();
		}
		// build the touch rect
		int[] pos = getPosition();
		int[] size = GetSize();
		m_BoundingRect.top = pos[1] - (size[1]);
		m_BoundingRect.bottom = m_BoundingRect.top + (size[1] * 2);
		m_BoundingRect.left = pos[0] - (size[0]);
		m_BoundingRect.right = m_BoundingRect.left + (size[0] * 2);
	}
	
	public void UpdateUV(_AnimationTextureLink animLink, boolean bMirror)
	{
		// get the UV rect
		m_Texture =  animLink.GetTexture();
		RectF frameRect = animLink.GetAnimation().GetFrameUV(m_Texture.GetWidth(), m_Texture.GetHeight(), bMirror);
		
		// put the rect where it belongs in the vertices
		// top right
		m_TriangleVerticesData[3] = frameRect.right;
		m_TriangleVerticesData[4] = frameRect.top;
 
		// bottom right
		m_TriangleVerticesData[8] = frameRect.right;
		m_TriangleVerticesData[9] = frameRect.bottom;

		// top left
		m_TriangleVerticesData[13] = frameRect.left;
		m_TriangleVerticesData[14] = frameRect.top;

		// bottom left
		m_TriangleVerticesData[18] = frameRect.left;
		m_TriangleVerticesData[19] = frameRect.bottom;

		// save
		m_TriangleVertices.position(0);
		m_TriangleVertices.put(m_TriangleVerticesData).position(0);
	}
	

	// return RectF of the collision.  else return null
	@Override
	public RectF CheckCollision(INyanObject other)
	{
		RectF rect = new RectF();
		CRectangle nyanRect = (CRectangle) other;
		
		boolean bIntersects = rect.setIntersect(m_BoundingRect, nyanRect.m_BoundingRect);
		if(bIntersects == false)
		{
			return null;
		}		
		
		return rect;
	}

	@Override
	public void HandleCollision(INyanObject other, RectF overlap)
	{
		// do nothing
	}


	@Override
	public boolean HandleTouch(int nPosX, int nPosY, int nTouchAction) {
		// TODO Auto-generated method stub
		return false;
	}


	@Override
	public Type GetType() 
	{
		// TODO Auto-generated method stub
		return Type.RECT;
	}
    
}












